home *** CD-ROM | disk | FTP | other *** search
/ ShareWare OnLine 2 / ShareWare OnLine Volume 2 (CMS Software)(1993).iso / ham / cat990.zip / RS232.C < prev    next >
C/C++ Source or Header  |  1993-02-01  |  17KB  |  500 lines

  1. /***
  2.  
  3.       RS232: Set of general purpose functions providing fully buffered
  4.       interrupt driven serial I/0.  Supports baud rates from 110 to
  5.       115.2K on serial ports 1 - 4.  Compiled and tested with Turbo C
  6.       and Borland C 2.0.  Note - do not compile with stack overflow
  7.       checking on!
  8.  
  9.       3/5/92
  10.       C. Karcher
  11.       Seattle WA
  12.       CSID 76406,536
  13.  
  14. ***/
  15.  
  16. #if !defined(__DOS_H)
  17. #include<dos.h>        /* need dos.h for port i/o and interrupt functions */
  18. #endif
  19.  
  20. /*****************************************************************/
  21. /***          global variables - all names begin with rs_      ***/
  22. /*****************************************************************/
  23. void interrupt (*rs_oldvec)(void) = NULL;
  24. int rs_intno,rs_thr,rs_rbr,rs_iir,rs_ier;
  25. unsigned rs_ibufsiz,rs_obufsiz;
  26. unsigned volatile rs_inhead,rs_intail,rs_outhead,rs_outtail;
  27. char *rs_inbuf,*rs_outbuf,rs_oldmask;
  28.  
  29. /*****************************************************************/
  30. /***                   function prototypes                     ***/
  31. /*****************************************************************/
  32.      /* interrupt handler */
  33. void interrupt rs_inthndlr(void);
  34.  
  35. /*------------------cut here for function quick reference-------------------*/
  36.  
  37.      /* port initialization: Sets up port parameters, installs interrupt
  38.         vector, enables interrupts.  Returns 0 on success.  Input and output
  39.         buffers are 'rotary' buffers - buffer size must be a power of 2.
  40.         Example:
  41.       status = rs_initport(port,baud,parity,bits,stop,in_bufsize,in_bufptr,\
  42.                            out_bufsize,out_bufptr);
  43.       port = '1','2','3', or '4'
  44.       baud = 110, 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600 or 115200
  45.       parity = 'N', 'E', 'O', 'S' or 'M'
  46.       bits = '7' or '8'
  47.       stop = '1' or '2'
  48.       in_bufsize = (power of 2) >= 2 <= 32768
  49.       in_bufptr = char pointer to previously allocated input buffer
  50.       out_bufsize = (power of 2) >= 2 <= 32768
  51.       out_bufptr = char pointer to previously allocated output buffer */
  52. int rs_initport(char, long, char, char, char,\
  53.                 unsigned, char *, unsigned, char *);
  54.  
  55.      /* Send single byte out port - if no room in output buffer, wait
  56.         til there's room. */
  57. void rs_sndbyt(char);
  58.  
  59.      /* Send string of specified length out port - if no room in output
  60.         buffer, waits til there's room. */
  61. void rs_sndstr(int,char *);
  62.  
  63.      /* Get single byte, wait if none available. */
  64. char rs_getbyt(void);
  65.  
  66.      /* Get newline terminated string (not to exceed specified number of
  67.         characters).  Include newline and null terminate.  Wait if
  68.         characters are unavailable.  Return number of characters read.*/
  69. int rs_getlin(int, char *);
  70.  
  71.      /* Get specified number of characters from input buffer.  Wait if
  72.         unavailable. */
  73. void rs_getstr(int, char *);
  74.  
  75.      /* Return number of characters waiting to be read from input buffer. */
  76. unsigned rs_inrcvd(void);
  77.  
  78.      /* Return amount of free space in output buffer. */
  79. unsigned rs_outfre(void);
  80.  
  81.      /* Call this function before exiting program or when finished with port.
  82.         Restores interrupt vector and interrupt mask, and disables port
  83.         interrupts. */
  84. void rs_close(void);
  85.  
  86. /*------------------cut here for function quick reference-------------------*/
  87.  
  88. /* Interrupt service routine - Get received character from port rs_rbr,
  89.    store in rs_inbuf and increment rs_inrcv */
  90. void interrupt rs_inthndlr(void)
  91. {
  92.  
  93.   extern void interrupt (*rs_oldvec)(void);
  94.   extern char *rs_inbuf,*rs_outbuf;
  95.   extern unsigned rs_ibufsiz,rs_obufsiz;
  96.   extern volatile unsigned rs_intail,rs_outhead,rs_outtail;
  97.   extern int rs_rbr,rs_iir,rs_thr;
  98.  
  99.   enable();  /* enable interrupts - code is reentrant */
  100.   _AL = inportb(rs_iir);   /* get interrupt id */
  101.   if(!(_AL & '\x01')){     /* interrupt pending ? */
  102.     do{
  103.       if((_AL & '\x04')){  /* data received interrupt ? */
  104.         *(rs_inbuf + rs_intail++) = inportb(rs_rbr); /* get the byte */
  105.         rs_intail &= rs_ibufsiz; /* in tail pointer wrapped around */
  106.         }
  107.       else if(rs_outhead != rs_outtail){ /* send a byte if any to send */
  108.         outportb(rs_thr,*(rs_outbuf + rs_outhead++));
  109.         rs_outhead &= rs_obufsiz;
  110.         }
  111.       }while(!((_AL = inportb(rs_iir)) & '\x01')); /* loop if int. pending */
  112.     }
  113.   else
  114.     rs_oldvec(); /* no interrupt pending on entry so this ain't our interrupt */
  115.  
  116.   outportb('\x20','\x20'); /* acknowledge 8259 */
  117.  
  118. }
  119.  
  120. /* rs_initport: Initialize port, interrupt vector and interrupt mask (see
  121.    description with function prototype for details).  Return 0 on success
  122.    and -1 on failure. */
  123. int rs_initport(char rs_port,long rs_baud,char rs_parity,char rs_bits,\
  124.                 char rs_stop,unsigned rs_userinbufsiz, char *rs_userinbuf,\
  125.                 unsigned rs_useroutbufsiz,char *rs_useroutbuf)
  126. {
  127.  
  128.   extern void interrupt (*rs_oldvec)(void);
  129.   void interrupt (*rs_intfnc)(void);
  130.   extern unsigned rs_obufsiz,rs_ibufsiz;
  131.   extern volatile unsigned rs_inhead,rs_intail,rs_outhead,rs_outtail;
  132.   extern int rs_intno,rs_thr,rs_rbr,rs_iir,rs_ier;
  133.   extern char rs_oldmask;
  134.   extern char *rs_inbuf,*rs_outbuf;
  135.   int rs_dll,rs_dlm,rs_lcr,rs_msr,rs_mcr,rs_lsr,rs_portbase;
  136.   char rs_dvsrl,rs_dvsrh,rs_mask;
  137.  
  138.  
  139.   if(rs_oldvec != NULL) /* if there's already a port open, forget it */
  140.     return -1;
  141.   rs_oldmask = '\0';
  142.  
  143.   /* make sure buffer size is valid */
  144.   if((rs_userinbufsiz - 1) & rs_userinbufsiz)
  145.     return -1;
  146.   rs_ibufsiz = rs_userinbufsiz - 1;
  147.   if((rs_inbuf = rs_userinbuf) == NULL)
  148.     return -1;
  149.   if((rs_useroutbufsiz - 1) & rs_useroutbufsiz)
  150.     return -1;
  151.   rs_obufsiz = rs_useroutbufsiz - 1;
  152.   if((rs_outbuf = rs_useroutbuf) == NULL)
  153.     return -1;
  154.  
  155.   /* initialize buffer head and tail pointers */
  156.   rs_inhead = rs_intail = rs_outhead = rs_outtail = 0;
  157.  
  158.   switch(rs_port){ /* find i/o port address, interrupt number mask */
  159.     case '1':
  160.       rs_portbase = 0x3F8;
  161.       rs_intno = 0x0C;
  162.       rs_mask = '\xEF';
  163.       break;
  164.     case '2':
  165.       rs_portbase = 0x2F8;
  166.       rs_intno = 0x0B;
  167.       rs_mask = '\xF7';
  168.       break;
  169.     case '3':
  170.       rs_portbase = 0x3E8;
  171.       rs_intno = 0x0C;
  172.       rs_mask = '\xEF';
  173.       break;
  174.     case '4':
  175.       rs_portbase = 0x2E8;
  176.       rs_intno = 0x0B;
  177.       rs_mask = '\xF7';
  178.       break;
  179.     default:
  180.       return -1;
  181.     }
  182.  
  183.   switch(rs_parity){
  184.     case 'N':
  185.       rs_parity = 0;
  186.       break;
  187.     case 'E':
  188.       rs_parity = '\x18';
  189.       break;
  190.     case 'O':
  191.       rs_parity = '\x08';
  192.       break;
  193.     case 'S':
  194.       rs_parity = '\x38';
  195.       break;
  196.     case 'M':
  197.       rs_parity = '\x28';
  198.       break;
  199.     default:
  200.       return -1;
  201.     }
  202.  
  203.   if(rs_bits == '7')
  204.     rs_bits = 2;
  205.   else if(rs_bits == '8')
  206.     rs_bits = 3;
  207.   else
  208.     return -1;
  209.  
  210.   if(rs_stop == '1')
  211.     rs_stop = 0;
  212.   else if(rs_stop == '2')
  213.     rs_stop = 4;
  214.   else
  215.     return -1;
  216.  
  217.   /* 8250 (or 16x50) registers: */
  218.   /* out, bit 7 of LCR = 0, (Transmit Holding Register) */
  219.   rs_thr = rs_portbase + 0;
  220.   /* in, bit 7 of LCR = 0, (Receive Buffer Register) */
  221.   rs_rbr = rs_portbase + 0;
  222.   /* out, bit 7 of LCR = 1, (Divisor Latch LSB) */
  223.   rs_dll = rs_portbase + 0;
  224.   /* out, bit 7 of LCR = 1, (Divisor Latch MSB) */
  225.   rs_dlm = rs_portbase + 1;
  226.   /* out, bit 7 of LCR = 0, (Interrupt Enable Register)
  227.                         bit 0 = 1 data rcvd
  228.                         bit 1 = 1 transmit holding reg. empty - the act of
  229.                                   setting this bit will generate the interrupt
  230.                         bit 2 = 1 data reception error
  231.                         bit 3 = 1 change in modem status
  232.                         bit 4-7 unused */
  233.   rs_ier = rs_portbase + 1;
  234.   /* in, (Interrupt ID register)
  235.                         bit  0  :  0 = interrupt pending
  236.                         bits 2-1: 00 = modem status change - read status
  237.                                   01 = transmit ready - output character or
  238.                                        read iir to clear
  239.                                   10 = data rcvd - read data
  240.                                   11 = break or error */
  241.   rs_iir = rs_portbase + 2;
  242.   /* out, (Line Control Register)
  243.                         rs_bits 0-1: Character Length
  244.                                      00 = 5 bits
  245.                                      01 = 6 bits
  246.                                      10 = 7 bits
  247.                                      11 = 8 bits
  248.                         bit 2: Number of stop bits
  249.                                       0 = 1 (1.5 if character length 5)
  250.                                       1 = 2
  251.                         bit 3: Parity
  252.                                       0 = no parity
  253.                                       1 = parity generated
  254.                         bit 4: Parity type
  255.                                       0 = odd
  256.                                       1 = even
  257.                         bit 5: Stick Parity
  258.                                       0 = disabled
  259.                                       1 = always 1 if bit 3 = 1 & bit 4 = 0 or
  260.                                           always 0 if bit 3 = 1 & bit 4 = 1 or
  261.                                           no parity if bit 3 = 0
  262.                         bit 6: Set Break
  263.                                       0 = disabled
  264.                                       1 = output string of 0s
  265.                         bit 7: Enable write to baud divisor regs. if 1 */
  266.   rs_lcr = rs_portbase + 3;
  267.   /* out, (Modem Control Register)
  268.                         bit 0:        1 = data terminal ready
  269.                         bit 1:        1 = request to send
  270.                         bit 2:        1 = aux. output 1
  271.                         bit 3:        1 = aux. output 2
  272.                         bit 4:        1 = UART loopback mode
  273.                         bit 5-7:      always 0 */
  274.   rs_mcr = rs_portbase + 4;
  275.   /* in, (Line Status Register)
  276.                         bit 0:        1 = character received
  277.                         bit 1:        1 = rcvd data overrun
  278.                         bit 2:        1 = parity error
  279.                         bit 3:        1 = framing error
  280.                         bit 4:        1 = break detected
  281.                         bit 5:        1 = transmit holding reg. empty
  282.                         bit 6:        1 = transmit shift reg. empty
  283.                         bit 7:        1 = time-out (off line) */
  284.   rs_lsr = rs_portbase + 5;
  285.   /* in, (Modem Status Register)
  286.                         bit 0:        1 = change in clear to send
  287.                         bit 1:        1 = change in data set ready
  288.                         bit 2:        1 = change in ring indicator
  289.                         bit 3:        1 = change in data carrier detect
  290.                         bit 4:        1 = clear to send
  291.                         bit 5:        1 = data set ready
  292.                         bit 6:        1 = ring indicator
  293.                         bit 7:        1 = data carrier detect */
  294.   rs_msr = rs_portbase + 6;
  295.  
  296.   /* get the baud rate divisor values */
  297.   rs_dvsrh = 0;
  298.   switch(rs_baud){
  299.     case 110L:
  300.       rs_dvsrh = '\x04';
  301.       rs_dvsrl = '\x17';
  302.       break;
  303.     case 300L:
  304.       rs_dvsrh = '\x01';
  305.       rs_dvsrl = '\x80';
  306.       break;
  307.     case 600L:
  308.       rs_dvsrl = '\xC0';
  309.       break;
  310.     case 1200L:
  311.       rs_dvsrl = '\x60';
  312.       break;
  313.     case 2400L:
  314.       rs_dvsrl = '\x30';
  315.       break;
  316.     case 4800L:
  317.       rs_dvsrl = '\x18';
  318.       break;
  319.     case 9600L:
  320.       rs_dvsrl = '\x0C';
  321.       break;
  322.     case 19200L:
  323.       rs_dvsrl = '\x06';
  324.       break;
  325.     case 38400L:
  326.       rs_dvsrl = '\x03';
  327.       break;
  328.     case 57600L:
  329.       rs_dvsrl = '\x02';
  330.       break;
  331.     case 115200L:
  332.       rs_dvsrl = '\x01';
  333.       break;
  334.     default:
  335.       return -1;
  336.     }
  337.  
  338.   rs_oldvec = getvect(rs_intno); /* get the old interrupt vector */
  339.   setvect(rs_intno,rs_inthndlr); /* plug in the new one */
  340.  
  341.   outportb(rs_ier,0);      /* disable UART interrupts */
  342.   outportb(rs_lcr,'\x80'); /* enable baud rate divisor registers */
  343.   outportb(rs_dll,rs_dvsrl); /* write divisor lo byte */
  344.   outportb(rs_dlm,rs_dvsrh); /* write divisor hi byte */
  345.   outportb(rs_lcr,(rs_parity | rs_bits | rs_stop)); /* write misc. parameters */
  346.   outportb(rs_mcr,'\x0B'); /* turn on RTS and DTR lines */
  347.   outportb(rs_ier,'\x03'); /* enable data rcvd hardware interrupts */
  348.   inportb(rs_iir); /* read out...*/
  349.   inportb(rs_rbr); /*...any garbage...*/
  350.   inportb(rs_lsr); /*...left in...*/
  351.   inportb(rs_msr); /*...registers */
  352.  
  353.   disable();
  354.   rs_oldmask = inportb(0x21);   /* save old interrupt controller mask */
  355.   rs_mask &= rs_oldmask;
  356.   outportb(0x21,rs_mask); /* interrupt now enabled */
  357.   enable();
  358.  
  359.   return 0;
  360.  
  361. }
  362.  
  363. /* rs_close: Restore original 8259 interrupt controller mask value, disable
  364.    UART interrupts and restore original interrupt vector. */
  365. void rs_close(void)
  366. {
  367.  
  368.   extern void interrupt (*rs_oldvec)(void);
  369.   extern int rs_intno,rs_ier;
  370.   extern char rs_oldmask;
  371.  
  372.   if(rs_oldmask){
  373.     disable();
  374.     outportb(0x21,rs_oldmask);    /* restore old interrupt mask value */
  375.     enable();
  376.     }
  377.   outportb(rs_ier,0);             /* disable UART interrupts */
  378.   if(rs_oldvec != NULL)
  379.     setvect(rs_intno,rs_oldvec);  /* restore old interrupt vector */
  380.   rs_oldvec = NULL;
  381.  
  382. }
  383.  
  384. /* rs_sndbyt: Output byte via output buffer.  If no space in output buffer,
  385.    wait til there is. */
  386. void rs_sndbyt(char rs_snd)
  387. {
  388.  
  389.   extern char *rs_outbuf;
  390.   extern int rs_ier;
  391.   extern volatile unsigned rs_outhead,rs_outtail;
  392.   extern unsigned rs_obufsiz;
  393.  
  394.   while(((rs_outtail + 1)  & rs_obufsiz) == rs_outhead)
  395.     ; /* make sure there's room in the buffer */
  396.   *(rs_outbuf + rs_outtail++) = rs_snd;
  397.   rs_outtail &= rs_obufsiz;
  398.   outportb(rs_ier,'\x03'); /* generate an interrupt to send the char */
  399.  
  400. }
  401.  
  402. /* rs_sndstr: Output rs_outcnt chars from rs_str to output buffer.  If not
  403.    enough space in output buffer, wait til there is.*/
  404. void rs_sndstr(int rs_sndcnt, char *rs_str)
  405. {
  406.  
  407.   extern char *rs_outbuf;
  408.   extern volatile unsigned rs_outhead,rs_outtail;
  409.   extern unsigned rs_obufsiz;
  410.   int rs_x;
  411.  
  412.  
  413.   for(rs_x = 0;rs_x < rs_sndcnt;rs_x++){
  414.     while(((rs_outtail + 1)  & rs_obufsiz) == rs_outhead)
  415.       ; /* make sure there's room in the buffer */
  416.     *(rs_outbuf + rs_outtail++) = *(rs_str + rs_x);
  417.     rs_outtail &= rs_obufsiz;
  418.     }
  419.  
  420.   outportb(rs_ier,'\x03'); /* generate an interrupt to start buffer transmit */
  421.  
  422. }
  423.  
  424. /* rs_outfre: Return amount of free space available in output buffer. */
  425. unsigned rs_outfre(void)
  426. {
  427.  
  428.   extern volatile unsigned rs_outhead,rs_outtail;
  429.   extern unsigned rs_obufsiz;
  430.  
  431.   return(rs_obufsiz + 1 - ((rs_outtail - rs_outhead) & rs_obufsiz));
  432.  
  433. }
  434.  
  435. /* rs_getbyt: Return character byte from input buffer - wait if none avail. */
  436. char rs_getbyt(void)
  437. {
  438.  
  439.   extern char *rs_inbuf;
  440.   extern volatile unsigned rs_inhead,rs_intail;
  441.   extern unsigned rs_ibufsiz;
  442.   char rs_byt;
  443.  
  444.   while(rs_inhead == rs_intail) /* wait if no char available */
  445.     ;
  446.   rs_byt = *(rs_inbuf + rs_inhead++);
  447.   rs_inhead &= rs_ibufsiz;
  448.   return rs_byt;
  449.  
  450. }
  451.  
  452. /*
  453.    rs_getlin: Get a maximum of 'cnt' characters from port buffer and place in
  454.    buffer 'rs_getbuf'.  Stop if newline is encountered.  Include new line in
  455.    array.  Returns number of characters copied to buf (excluding term '\0').
  456. */
  457. int rs_getlin(int rs_getcnt,char *rs_getbuf)
  458. {
  459.   int rs_x = 0;
  460.   char rs_c;
  461.  
  462.   while(rs_x < rs_getcnt){
  463.     rs_c = rs_getbyt();
  464.     *(rs_getbuf + rs_x) = rs_c;
  465.     rs_x++;
  466.     if(rs_c == '\n' || rs_x == rs_getcnt)
  467.       break;
  468.     }
  469.  
  470.   *(rs_getbuf + rs_x) = '\0';
  471.   return rs_x;
  472.  
  473. }
  474.  
  475. /* rs_getstr: Get specified number of bytes from port buffer and place in
  476.    user specified buffer.  Does not null terminate string.  If specified
  477.    number of input chars are not available, waits til they are. */
  478. void rs_getstr(int rs_getcnt,char *rs_getbuf)
  479. {
  480.   int rs_x = 0;
  481.  
  482.   while(rs_x < rs_getcnt)
  483.     *(rs_getbuf + rs_x++) = rs_getbyt();
  484.  
  485. }
  486.  
  487. /* rs_inrcvd: Return number of received bytes waiting to be read from input
  488.    buffer. */
  489. unsigned rs_inrcvd(void)
  490. {
  491.  
  492.   unsigned rs_incnt;
  493.  
  494.   extern volatile unsigned rs_inhead,rs_intail;
  495.   extern unsigned rs_ibufsiz;
  496.  
  497.   return((rs_intail - rs_inhead) & rs_ibufsiz);
  498.  
  499. }
  500.